<template>
    <a-popup ref="popupRef" popPlacement="right" :popModifiers="popModifiers" v-model:popShown="state.popShown" :escToClose="false" :trigger="!formItemHook.myDisabled && props.clearable ? 'hover' : 'none'">
        <input class="a-input-string" :class="[
        `color-${formItemHook.myColor}`,
        `size-${formItemHook.mySize}`,
        { sharp: formItemHook.mySharp },
        { disabled: formItemHook.myDisabled },
    ]" :type="props.type" :disabled="formItemHook.myDisabled" :readonly="props.readonly" :value="props.value" :placeholder="props.placeholder" title="" @input="methods.input(($event.target as any).value)" @focus="emit('focus')" @blur="$emit('blur')" ref="inputRef" />
        <template v-slot:pop>
            <a-button :color="formItemHook.myColor" :size="formItemHook.mySize" :square="true" :sharp="formItemHook.mySharp" :link="true" @click="methods.clear">
                <span class="fa fa-fw fa-remove"></span>
            </a-button>
        </template>
    </a-popup>
</template>

<style lang="scss" scoped>
@import "../scss/vars";

.a-input-string {
    box-sizing: border-box;
    outline: none;
    transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;

    &.disabled {
        opacity: 0.5;
        cursor: not-allowed;
    }

    @each $colorPackageName, $colorPackageValue in $colorPackages {
        $bgColor: map-get($colorPackageValue, "bgColor");

        &.color-#{$colorPackageName} {
            background-color: $defaultBgColor;
            border: solid 1px $bgColor;

            &:focus:not(.disabled) {
                border-color: darken($bgColor, 20%);
                box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075),
                    0 0 8px lighten($bgColor, 10%);
            }
        }
    }

    @each $sizePackageName, $sizePackageValue in $sizePackages {
        &.size-#{$sizePackageName} {
            padding-left: map-get($sizePackageValue, "space");
            padding-right: map-get($sizePackageValue, "space");
            font-size: map-get($sizePackageValue, "fontSize");
            height: map-get($sizePackageValue, "size");

            &:not(.sharp) {
                border-radius: map-get($sizePackageValue, "borderRadius");
            }
        }
    }

    &[type="number"]::-webkit-outer-spin-button {
        -webkit-appearance: none;
    }

    &[type="number"]::-webkit-inner-spin-button {
        -webkit-appearance: none;
    }

    &[type="number"] {
        appearance: textfield;
    }
}
</style>

<script setup lang="ts">
import { FormItemProvided, InputPropsType, PopupExposed, Rule_maxLength, Rule_minLength } from '@/types'
import AButton from './AButton.vue'
import APopup from './APopup.vue'
import { computed, reactive, onMounted, ref, watch, inject } from 'vue'
import utils from '@/common/utils'
import { useInput } from '@/hooks/useInput'
import { useFormItem } from '@/hooks/useFormItem'

interface PropsType extends InputPropsType<string | undefined, Rule_minLength | Rule_maxLength> {
    type?: 'text' | 'password' | 'number'
    readonly?: boolean
    clearable?: boolean
    placeholder?: string
    autoFocus?: boolean
}
const props = withDefaults(defineProps<PropsType>(), {
    value: undefined,
    color: undefined,
    disabled: undefined,
    size: undefined,
    sharp: undefined,
    rules: () => [],
    isInner: false,
    type: 'text',
    readonly: false,
    clearable: true,
    placeholder: undefined,
    autoFocus: false
})

interface StateType {
    popShown: boolean
}
const state = reactive<StateType>({
    popShown: false
})

const emit = defineEmits<{
    (e: 'update:value', value?: string): void
    (e: 'clear'): void
    (e: 'validate', result: [boolean?, string?]): Promise<void>
    (e: 'focus'): void
    (e: 'blur'): void
}>()
const inputHook = useInput(props)
const formItemHook = useFormItem(props)
const formItem = inject<FormItemProvided | undefined>('formItem', undefined)

const sizePackage = computed(() => {
    return utils.getSizePackageByName(formItemHook.value.mySize)
})
const popModifiers = computed(() => {
    return [
        {
            name: 'offset',
            options: {
                offset: [0, -sizePackage.value.size],
            }
        },
        {
            name: 'preventOverflow',
            options: {
                mainAxis: false,
                altAxis: false,
            }
        }
    ]
})
const diff = computed(() => {
    if (props.rules === undefined) {
        return undefined
    }
    let maxLengthRule = props.rules.find(a => a.name === 'maxLength') as Rule_maxLength | undefined
    if (!maxLengthRule) {
        return undefined
    }
    let valueLength = props.value?.length ?? 0
    return {
        valid: valueLength <= maxLengthRule.value,
        text: `${valueLength}/${maxLengthRule.value}`
    }
})
watch(() => inputHook.validator.valid, () => {
    if (props.isInner) {
        return
    }
    emit('validate', [inputHook.validator.valid, inputHook.validator.errorMessage])
})
watch(() => diff.value, () => {
    methods.setDiffResult()
})
onMounted(() => {
    methods.setDiffResult()
    methods.setFocus()
})
const popupRef = ref<PopupExposed>()
const inputRef = ref<HTMLInputElement>()
const methods = {
    input(val?: string) {
        emit('update:value', val)
    },
    getTargetEl() {
        return popupRef.value?.getTargetEl()
    },
    getPopEl() {
        return popupRef.value?.getPopEl()
    },
    clear() {
        inputRef.value!.value = ''
        methods.input(undefined)
        emit('clear')
        methods.getTargetEl()?.focus()
    },
    setDiffResult() {
        formItem?.value.setDiffResult(diff.value?.valid, diff.value?.text)
    },
    setFocus() {
        if (props.autoFocus) {
            methods.getTargetEl()?.focus()
        }
    }
}
</script>